/*
 * timer_ms.c
 *
 *  Created on: 13 May 2015
 *      Author: yiqi.pan
 */

#include "common.h"
#include "stm32f0xx_conf.h"
#include "timer_ms.h"
//! \brief TC Timeout timekeeping data
struct timer_ms_struct {
	/**
	 * Current count-down value. Counts down for every tick.
	 * Will be considered as expired when it reaches 0, and
	 * may then be reloaded with period.
	 */
	uint16_t count;
	/**
	 * Period between expires. Used to reload count.
	 * If 0, the count won't be reloaded.
	 */
	uint16_t period;
};

// Validate number of timeouts configured
#if TIMER_MS_COUNT > 8
# error "Too many timeouts configured! Maximum is 8"
#endif

#define TIMER_MS_PRELOAD_VALUE     131  // timer prescaler set to 128, clock in 16Mhz


/**
 * \brief Array of configurable timeout timekeeping data
 *
 * \see timer_ms_configuration
 */
static struct timer_ms_struct timer_ms_array[TIMER_MS_COUNT];

//! \brief Bitmask of active timeouts
static uint8_t timer_ms_active;

//! \brief bitmask of expired timeouts
static uint8_t timer_ms_expired;

/**
 * \brief Timer Counter Overflow interrupt callback function
 *
 * This function is called when an overflow interrupt has occurred
 */
//void timeout_callback(void)
void TIM14_IRQHandler(void)
{
	uint8_t i;

	TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
	for (i = 0; i < TIMER_MS_COUNT; i++)
	{
		if (!(timer_ms_active & (1 << i)))
		{
			continue;
		}
        else
        {
            timer_ms_array[i].count--;
            if (timer_ms_array[i].count)
            {
                timer_ms_array[i].count--;  //make it a 2ms timer
                if (timer_ms_array[i].count)
                {
                    continue;
                }
            }

            {
                timer_ms_expired |= 1 << i;
                if (timer_ms_array[i].period)
                {
                    timer_ms_array[i].count = timer_ms_array[i].period;
                }
                else
                {
                    timer_ms_active &= ~(1 << i);
                }
            }
        }
	}
}



void timer_ms_init(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
  TIM_OCInitTypeDef  TIM_OCInitStructure;
  

  /* TIM3 clock enable */
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

  /* Enable the TIM3 gloabal Interrupt */
  NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  
   /* -----------------------------------------------------------------------
    TIM3 Configuration: Output Compare Timing Mode:
    
    In this example TIM3 input clock (TIM3CLK) is set to APB1 clock (PCLK1),  
      => TIM3CLK = PCLK1 = SystemCoreClock = 48 MHz
          
    To get TIM3 counter clock at 6 MHz, the prescaler is computed as follows:
       Prescaler = (TIM3CLK / TIM3 counter clock) - 1
       Prescaler = (PCLK1 /6 MHz) - 1
                                                  
    CC3 update rate = TIM3 counter clock / CCR3_Val = 439.4 Hz
    ==> Toggling frequency = 219.7 Hz
    
    CC4 update rate = TIM3 counter clock / CCR4_Val = 878.9 Hz
    ==> Toggling frequency = 439.4 Hz

    Note: 
     SystemCoreClock variable holds HCLK frequency and is defined in system_stm32f2xx.c file.
     Each time the core clock (HCLK) changes, user had to call SystemCoreClockUpdate()
     function to update SystemCoreClock variable value. Otherwise, any configuration
     based on this variable will be incorrect.    
  ----------------------------------------------------------------------- */   

  /* Time base configuration */
  TIM_TimeBaseStructure.TIM_Period = 10000-1;
  TIM_TimeBaseStructure.TIM_Prescaler = 4200-1;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_TimeBaseInit(TIM14, &TIM_TimeBaseStructure);

  /* Output Compare Timing Mode configuration: Channel1 */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;

  /* Output Compare Timing Mode configuration: Channel3 */
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
  TIM_OCInitStructure.TIM_Pulse = 62100;
  
  TIM_OC1Init(TIM14, &TIM_OCInitStructure);

  TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Disable);
   
  /* TIM Interrupts enable */
  TIM_ITConfig(TIM14, TIM_IT_Update , ENABLE);

  /* TIM3 enable counter */
  TIM_Cmd(TIM14, ENABLE);
}

void timer_ms_stop(void)
{
	TIM_Cmd(TIM14, DISABLE);
	/* Disable TIM3 clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, DISABLE);
}

void timer_ms_start_offset(timer_id_t id, uint16_t period,
		uint16_t offset)
{
	TIM_ITConfig(TIM14, TIM_IT_CC1 , DISABLE);
	timer_ms_array[id].count = offset;
	timer_ms_array[id].period = period;
	timer_ms_active |= 1 << id;
	// Clear any pending expired in case of timeout restart
	timer_ms_expired &= ~(1 << id);
	TIM_ITConfig(TIM14, TIM_IT_CC1 , ENABLE);
}

void timer_ms_start_singleshot(timer_id_t id, uint16_t timeout)
{
	timer_ms_start_offset(id, 0, timeout);
}

void timer_ms_start_periodic(timer_id_t id, uint16_t period)
{
	timer_ms_start_offset(id, period, period);
}

bool timer_ms_test_and_clear_expired(timer_id_t id)
{
	if (timer_ms_expired & (1 << id))
	{
		// Clear expired flag safely if it's set
		TIM_ITConfig(TIM14, TIM_IT_CC1 , DISABLE);
		timer_ms_expired &= ~(1 << id);
		TIM_ITConfig(TIM14, TIM_IT_CC1 , ENABLE);
		return true;
	}
	return false;
}

void timer_ms_disactivate(timer_id_t id)
{
	TIM_ITConfig(TIM14, TIM_IT_CC1 , DISABLE);
	timer_ms_active &= ~(1 << id);
	TIM_ITConfig(TIM14, TIM_IT_CC1 , ENABLE);
}

bool is_active_timer_ms(timer_id_t id)
{
    return (timer_ms_active & ~(1 << id));
}


